﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SpiceNet.Circuits;
using SpiceNet.Diagnostics;
using SpiceNet.Parameters;

namespace SpiceNet.Models
{
    /// <summary>
    /// A class for a JFET model
    /// </summary>
    public class JfetModel : Model
    {
        /// <summary>
        /// Get parameters
        /// </summary>
        public override Dictionary<string, Parameterized.IP> ParamTable
        {
            get { return _pTable; }
        }

        #region Parameters
        private enum _c
        {
            /* model parameters */
            JFET_MOD_VTO = 101,
            JFET_MOD_BETA = 102,
            JFET_MOD_LAMBDA = 103,
            JFET_MOD_RD = 104,
            JFET_MOD_RS = 105,
            JFET_MOD_CGS = 106,
            JFET_MOD_CGD = 107,
            JFET_MOD_PB = 108,
            JFET_MOD_IS = 109,
            JFET_MOD_FC = 110,
            JFET_MOD_NJF = 111,
            JFET_MOD_PJF = 112,
            JFET_MOD_TNOM = 113,
            JFET_MOD_KF = 114,
            JFET_MOD_AF = 115,
            /* Modification for Sydney University JFET model */
            JFET_MOD_B = 116,
            /* end Sydney University mod */
            /* model questions */
            JFET_MOD_DRAINCONDUCT = 301,
            JFET_MOD_SOURCECONDUCT = 302,
            JFET_MOD_DEPLETIONCAP = 303,
            JFET_MOD_VCRIT = 304,
            JFET_MOD_TYPE = 305
        }
        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "type", new IP(IF.OP, (int)_c.JFET_MOD_TYPE, T.STRING, "N-type or P-type JFET model") },
            { "njf", new IP(IF.IOP, (int)_c.JFET_MOD_NJF, T.FLAG, "N type JFET model") },
            { "pjf", new IP(IF.IOP, (int)_c.JFET_MOD_PJF, T.FLAG, "P type JFET model") },
            { "vt0", new IP(IF.IOP, (int)_c.JFET_MOD_VTO, T.REAL, "Threshold voltage") },
            { "vto", new IP(IF.IOPR, (int)_c.JFET_MOD_VTO, T.REAL, "Threshold voltage") },
            { "beta", new IP(IF.IOP, (int)_c.JFET_MOD_BETA, T.REAL, "Transconductance parameter") },
            { "lambda", new IP(IF.IOP, (int)_c.JFET_MOD_LAMBDA, T.REAL, "Channel length modulation param.") },
            { "rd", new IP(IF.IOP, (int)_c.JFET_MOD_RD, T.REAL, "Drain ohmic resistance") },
            { "gd", new IP(IF.OPU, (int)_c.JFET_MOD_DRAINCONDUCT, T.REAL, "Drain conductance") },
            { "rs", new IP(IF.IOP, (int)_c.JFET_MOD_RS, T.REAL, "Source ohmic resistance") },
            { "gs", new IP(IF.OPU, (int)_c.JFET_MOD_SOURCECONDUCT, T.REAL, "Source conductance") },
            { "cgs", new IP(IF.IOPA, (int)_c.JFET_MOD_CGS, T.REAL, "G-S junction capactance") },
            { "cgd", new IP(IF.IOPA, (int)_c.JFET_MOD_CGD, T.REAL, "G-D junction cap") },
            { "pb", new IP(IF.IOP, (int)_c.JFET_MOD_PB, T.REAL, "Gate junction potential") },
            { "is", new IP(IF.IOP, (int)_c.JFET_MOD_IS, T.REAL, "Gate junction saturation current") },
            { "fc", new IP(IF.IOP, (int)_c.JFET_MOD_FC, T.REAL, "Forward bias junction fit parm.") },
             /* Modification for Sydney University JFET model */
            { "b", new IP(IF.IOP, (int)_c.JFET_MOD_B, T.REAL, "Doping tail parameter") },
             /* end Sydney University mod. */
            { "tnom", new IP(IF.IOPU, (int)_c.JFET_MOD_TNOM, T.REAL, "parameter measurement temperature") },
            { "kf", new IP(IF.IOP, (int)_c.JFET_MOD_KF, T.REAL, "Flicker Noise Coefficient") },
            { "af", new IP(IF.IOP, (int)_c.JFET_MOD_AF, T.REAL, "Flicker Noise Exponent") }
        };

        /// <summary>
        /// Optional parameters
        /// </summary>
        public int JFETtype { get; private set; }
        public Parameter<double> JFETthreshold { get; private set; }
        public Parameter<double> JFETbeta { get; private set; }
        public Parameter<double> JFETlModulation { get; private set; }
        public Parameter<double> JFETdrainResist { get; private set; }
        public Parameter<double> JFETsourceResist { get; private set; }
        public Parameter<double> JFETcapGS { get; private set; }
        public Parameter<double> JFETcapGD { get; private set; }
        public Parameter<double> JFETgatePotential { get; private set; }
        public Parameter<double> JFETgateSatCurrent { get; private set; }
        public Parameter<double> JFETdepletionCapCoeff { get; private set; }
        /* Modification for Sydney University JFET model */
        public Parameter<double> JFETb { get; private set; }
        /* end Sydney University mod */
        public Parameter<double> JFETtnom { get; private set; } /* user specified Tnom for model */
        public Parameter<double> JFETfNcoef { get; private set; }
        public Parameter<double> JFETfNexp { get; private set; }
        #endregion

        #region Calculated constants
        public double JFETdrainConduct { get; private set; }
        public double JFETsourceConduct { get; private set; }
        public double JFETf2 { get; private set; }
        public double JFETf3 { get; private set; }
        /* Modification for Sydney University JFET model */
        public double JFETbFac { get; private set; }  /* internal derived doping profile parameter */
        /* end Sydney University mod */
        #endregion

        #region Constants
        private const int NJF = 1;
        private const int PJF = -1;
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        public JfetModel(string name)
            : base(name)
        {
            JFETtype = NJF;
            JFETthreshold = new Parameter<double>(-2.0);
            JFETbeta = new Parameter<double>(1e-4);
            JFETlModulation = new Parameter<double>();
            JFETdrainResist = new Parameter<double>();
            JFETsourceResist = new Parameter<double>();
            JFETcapGS = new Parameter<double>();
            JFETcapGD = new Parameter<double>();
            JFETgatePotential = new Parameter<double>(1.0);
            JFETgateSatCurrent = new Parameter<double>(1.0e-14);
            JFETdepletionCapCoeff = new Parameter<double>(0.5);
            /* Modification for Sydney University JFET model */
            JFETb = new Parameter<double>();
            /* end Sydney University mod. */
            JFETtnom = new Parameter<double>();
            JFETfNcoef = new Parameter<double>();
            JFETfNexp = new Parameter<double>(1.0);
        }

        /// <summary>
        /// Set parameter
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.JFET_MOD_TNOM:
                    JFETtnom.Par((double)value + Circuit.CONSTCtoK);
                    break;
                case _c.JFET_MOD_VTO:
                    JFETthreshold.Par((double)value);
                    break;
                case _c.JFET_MOD_BETA:
                    JFETbeta.Par((double)value);
                    break;
                case _c.JFET_MOD_LAMBDA:
                    JFETlModulation.Par((double)value);
                    break;
                case _c.JFET_MOD_RD:
                    JFETdrainResist.Par((double)value);
                    break;
                case _c.JFET_MOD_RS:
                    JFETsourceResist.Par((double)value);
                    break;
                case _c.JFET_MOD_CGS:
                    JFETcapGS.Par((double)value);
                    break;
                case _c.JFET_MOD_CGD:
                    JFETcapGD.Par((double)value);
                    break;
                case _c.JFET_MOD_PB:
                    JFETgatePotential.Par((double)value);
                    break;
                case _c.JFET_MOD_IS:
                    JFETgateSatCurrent.Par((double)value);
                    break;
                case _c.JFET_MOD_FC:
                    JFETdepletionCapCoeff.Par((double)value);
                    break;
                case _c.JFET_MOD_NJF:
                    JFETtype = NJF;
                    break;
                case _c.JFET_MOD_PJF:
                    JFETtype = PJF;
                    break;
                case _c.JFET_MOD_KF:
                    JFETfNcoef.Par((double)value);
                    break;
                case _c.JFET_MOD_AF:
                    JFETfNexp.Par((double)value);
                    break;
                /* Modification for Sydney University JFET model */
                case _c.JFET_MOD_B:
                    JFETb.Par((double)value);
                    break;
                /* end Sydney University mod */
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Ask a parameter
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.JFET_MOD_TNOM:
                    return JFETtnom - Circuit.CONSTCtoK;
                case _c.JFET_MOD_VTO:
                    return JFETthreshold.Value;
                case _c.JFET_MOD_BETA:
                    return JFETbeta.Value;
                case _c.JFET_MOD_LAMBDA:
                    return JFETlModulation.Value;
                /* Modification for Sydney University JFET model */
                case _c.JFET_MOD_B:
                    return JFETb.Value;
                /* end Sydney University mod */
                case _c.JFET_MOD_RD:
                    return JFETdrainResist.Value;
                case _c.JFET_MOD_RS:
                    return JFETsourceResist.Value;
                case _c.JFET_MOD_CGS:
                    return JFETcapGS.Value;
                case _c.JFET_MOD_CGD:
                    return JFETcapGD.Value;
                case _c.JFET_MOD_PB:
                    return JFETgatePotential.Value;
                case _c.JFET_MOD_IS:
                    return JFETgateSatCurrent.Value;
                case _c.JFET_MOD_FC:
                    return JFETdepletionCapCoeff.Value;
                case _c.JFET_MOD_DRAINCONDUCT:
                    return JFETdrainConduct;
                case _c.JFET_MOD_SOURCECONDUCT:
                    return JFETsourceConduct;
                case _c.JFET_MOD_TYPE:
                    if (JFETtype == NJF)
                        return "njf";
                    else
                        return "pjf";
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Setup
        /// </summary>
        /// <param name="ckt"></param>
        public override void Setup(Circuit ckt)
        {
            if (JFETdrainResist != 0)
                JFETdrainConduct = 1.0 / JFETdrainResist;
            else
                JFETdrainConduct = 0;
            if (JFETsourceResist != 0)
                JFETsourceConduct = 1.0 / JFETsourceResist;
            else
                JFETsourceConduct = 0;
            base.Setup(ckt);
        }

        /// <summary>
        /// A structure with temperature-dependent parameters
        /// </summary>
        public class JfetTempParameters {
            public double cjfact, pbo, xfc;
        }

        /// <summary>
        /// Do temperature dependent calculations
        /// </summary>
        /// <param name="ckt"></param>
        public override void Temperature(Circuit ckt)
        {
            double vtnom;
            double kt1;
            double arg1;
            double fact1;
            double egfet1;
            double pbfact1;
            double gmaold;
            JfetTempParameters tp = new JfetTempParameters();

            if (!JFETtnom.Given)
                JFETtnom.Value = (ckt.Config.NominalTemperature);
            vtnom = Circuit.CONSTKoverQ * JFETtnom;
            fact1 = JFETtnom / Circuit.CONSTRefTemp;
            kt1 = Circuit.CONSTBoltz * JFETtnom;
            egfet1 = 1.16 - (7.02e-4 * JFETtnom * JFETtnom) /
                    (JFETtnom + 1108);
            arg1 = -egfet1 / (kt1 + kt1) + 1.1150877 / (Circuit.CONSTBoltz * (Circuit.CONSTRefTemp + Circuit.CONSTRefTemp));
            pbfact1 = -2 * vtnom * (1.5 * Math.Log(fact1) + Circuit.CHARGE * arg1);
            tp.pbo = (JFETgatePotential - pbfact1) / fact1;
            gmaold = (JFETgatePotential - tp.pbo) / tp.pbo;
            tp.cjfact = 1 / (1 + .5 * (4e-4 * (JFETtnom - Circuit.CONSTRefTemp) - gmaold));

            if (JFETdrainResist != 0)
            {
                JFETdrainConduct = 1 / JFETdrainResist;
            }
            else
            {
                JFETdrainConduct = 0;
            }
            if (JFETsourceResist != 0)
            {
                JFETsourceConduct = 1 / JFETsourceResist;
            }
            else
            {
                JFETsourceConduct = 0;
            }
            if (JFETdepletionCapCoeff > .95)
            {
                CircuitWarning.Warning(this, string.Format("{0}: Depletion cap. coefficient too large, limited to 0.95", Name));
                JFETdepletionCapCoeff.Value = 0.95;
            }

            tp.xfc = Math.Log(1 - JFETdepletionCapCoeff);
            JFETf2 = Math.Exp((1 + .5) * tp.xfc);
            JFETf3 = 1 - JFETdepletionCapCoeff * (1 + .5);
            /* Modification for Sydney University JFET model */
            JFETbFac = (1 - JFETb)
                / (JFETgatePotential - JFETthreshold);
            /* end Sydney University mod */

            foreach (var inst in Instances)
                inst.Temperature(ckt, this, tp);
        }
    }
}
